Skip to content

feat(core): add renderCopyButton render prop to CodeBlock - #4795

Closed
freddymeta wants to merge 1 commit into
mainfrom
feat/codeblock-render-copy-button
Closed

feat(core): add renderCopyButton render prop to CodeBlock#4795
freddymeta wants to merge 1 commit into
mainfrom
feat/codeblock-render-copy-button

Conversation

@freddymeta

Copy link
Copy Markdown
Contributor

What this does

Adds a renderCopyButton render prop to CodeBlock, letting a consumer supply its own copy control while CodeBlock keeps ownership of everything that isn't the button's appearance.

Why

CodeBlock's copy control is a bare <button> with no tooltip, and there's no seam to restyle or replace it: hasCopyButton is a plain boolean, and the component exposes a single theme target on the root <pre>. A design system consuming Astryx that wants its own copy control (e.g. its standard icon button with a "Copy" tooltip — a tooltip is DOM, so it can't be added via theming or CSS) is forced to set hasCopyButton={false} and then re-implement placement, the clipboard write, the copied-state timer, and the copy announcement, plus position the replacement with structural CSS against internal DOM. That's the "reach for an unsupported structural selector" signal that a sanctioned seam is missing.

A paint/theme target is the wrong tier here (the need is a different control, not different paint on the same one), and an imperative copy() ref was considered previously and set aside. A render prop is the right shape — it mirrors the existing renderOption / renderItem / renderToken convention and hands back exactly the state a copy control needs.

The seam

renderCopyButton?: (props: {
  isCopied: boolean;   // reflects the block's copied-state timer
  copy: () => void;    // runs the clipboard write + confirmation flow
  label: string;       // localized, state-aware accessible label
}) => React.ReactNode

CodeBlock still owns:

  • Placement — the control renders as the header's trailing child, or (headerless) in the floating corner, so the consumer supplies no positioning.
  • Clipboard write, the copied-state timer, the polite live-region announcement, and onCopy.

The render prop only supplies the visual control. Ignored when hasCopyButton is false.

<CodeBlock
  code={code}
  renderCopyButton={({isCopied, copy, label}) => (
    <IconButton
      label={label}
      icon={<Icon icon={isCopied ? 'check' : 'copy'} />}
      onClick={copy}
    />
  )}
/>

Scope

Additive and backward-compatible — the built-in button path is unchanged when renderCopyButton is omitted. No new theme target, no DOM change to existing usage.

Tests

Extended CodeBlock.test.tsx with a renderCopyButton block: custom control replaces the built-in button, copy/isCopied/label drive the block's clipboard + copied flow, the announcement and onCopy still fire, hasCopyButton={false} suppresses it, and the custom control stays out of the collapsible header's role="button". 22/22 CodeBlock tests, core typecheck, storybook typecheck, docsite (324), and strict lint all green. Added a CustomCopyButton Storybook story.

CodeBlock's copy control was a bare, unstyleable <button> with no way to
replace it — the component exposed only the root theme target, so consumers
who needed a different copy control (e.g. one with a tooltip) had to disable
hasCopyButton and re-implement placement, clipboard, and copied-state
themselves.

Add a renderCopyButton render prop: the block keeps ownership of placement
(header, or floating corner when headerless), the clipboard write, the
copied-state timer, and the polite copy announcement; the render prop only
supplies the visual control, wired to copy/isCopied/label. Ignored when
hasCopyButton is false.
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
astryx Ready Ready Preview Aug 7, 2026 2:36pm

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 7, 2026
@github-actions github-actions Bot added community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge needs:design-review Affects visuals — Design should review labels Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

🧪 Sandbox Preview

View Sandbox for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

Modified Components

CodeBlock (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Bundle Size (ESM) N/A N/A N/A
Lines of Code N/A 1604 -
Complexity N/A Very High (90) -

Bundle Size Summary

Package Size (ESM) Size (CJS) Gzipped
@astryxdesign/core N/A 4.7KB 1.2KB

Accessibility Audit

Status: No accessibility violations detected.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

github-actions Bot added a commit that referenced this pull request Aug 7, 2026
@freddymeta

Copy link
Copy Markdown
Contributor Author

Closing — wrong shape for the actual need.

The goal was to upstream CodeBlock's copy button so consumers stop re-implementing it (bare <button>, no tooltip, not themeable). A renderCopyButton render prop does the opposite — it makes the consumer bring their own control and re-wire placement/clipboard, rather than Astryx shipping a good built-in one.

The right fix is to improve CodeBlock's own copy button — a default "Copy"/"Copied" tooltip and a theme target so it's restyleable — backed by an extracted useClipboard hook (which also de-duplicates the identical clipboard/copied-state/announce logic in Timestamp). Superseding this with that work; will link the spec issue here.

@freddymeta freddymeta closed this Aug 10, 2026
@freddymeta
freddymeta deleted the feat/codeblock-render-copy-button branch August 10, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants